﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using NAudio.MediaFoundation;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using NVorbis.NAudioSupport;
using OggToMp3.Properties;

namespace OggToMp3
{
    public partial class Form1 : Form
    {
        private readonly Queue<string> _playList;
        private string _outputFolder;
        private VorbisFileReader _reader;
        private WaveOut _waveOut;
        private BackgroundWorker _bw;
        private string[] _files;

        public Form1()
        {
            InitializeComponent();
            _playList = new Queue<string>();
        }

        protected string SelectedPath { get; set; }

        private void button1_Click(object sender, EventArgs e)
        {
            DialogResult result = folderBrowserDialog1.ShowDialog();


            if (result == DialogResult.OK)
            {
                LoadFiles(folderBrowserDialog1.SelectedPath);
            }
        }

        private void LoadFiles(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                MessageBox.Show(Resources.Form1_LoadFiles_No_folder_selected_);
                return;
            }
            path = path.Replace(Environment.NewLine, "");
            SelectedPath = path;
            textBox1.Text = path;
            try
            {
                _files = Directory.GetFiles(path, "*.ogg");
            }
            catch (ArgumentException)
            {
                MessageBox.Show(Resources.Form1_LoadFiles_Invalid_folder_selected_);
                return;
            }
            catch (DirectoryNotFoundException)
            {
                MessageBox.Show(Resources.Form1_LoadFiles_Invalid_folder_selected_);
                return;
            }
            listView1.Items.Clear();

            foreach (string file in _files)
            {
                string fileName = Path.GetFileName(file);
                var item = new ListViewItem(fileName) {Tag = file};

                listView1.Items.Add(item);
            }
            lblTotal.Text = listView1.Items.Count.ToString();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            DisableScreen();
            CreateOutputFolder();
            StartFileSystemWatcher();
            ClearConvertResult();
            var inputFiles = new string[listView1.CheckedItems.Count];
            var outputFiles = new string[listView1.CheckedItems.Count];

            for (int i = 0; i < listView1.CheckedItems.Count; i++)
            {
                ListViewItem item = listView1.CheckedItems[i];
                inputFiles[i] = item.Tag.ToString();
                outputFiles[i] = OutputFilePath(item);
            }
            StartConverting(inputFiles, outputFiles);
        }

        private void ClearConvertResult()
        {
            lvError.Items.Clear();
            listView2.Items.Clear();
        }

        private void DisableScreen()
        {
            textBox1.Enabled = false;
            listView1.Enabled = false;
            button1.Enabled = false;
            btnPlay.Enabled = false;
            btnStop.Enabled = false;
            btnCheckAll.Enabled = false;
            btnInverseCheck.Enabled = false;
            btnUnCheckAll.Enabled = false;
            button2.Enabled = false;
        }

        private void EnableScreen()
        {
            textBox1.Enabled = true;
            listView1.Enabled = true;
            button1.Enabled = true;
            btnPlay.Enabled = true;
            btnStop.Enabled = true;
            btnCheckAll.Enabled = true;
            btnInverseCheck.Enabled = true;
            btnUnCheckAll.Enabled = true;
            button2.Enabled = true;
        }

        public void StartConverting(string[] inputFiles, string[] outputFiles)
        {
            _bw = new BackgroundWorker();


            _bw.DoWork += (sender, args) => ConvertAllCheckedItems(inputFiles, outputFiles);
            _bw.ProgressChanged += bw_ProgressChanged;
            _bw.RunWorkerCompleted += (sender, args) =>
                {
                    if (args.Error != null)
                        MessageBox.Show(args.Error.ToString());

                    MessageBox.Show(Resources.Form1_startConverting_Converting_is_completed);
                    EnableScreen();
                    StopFileSystemWatcher();
                };
            _bw.WorkerReportsProgress = true;
            _bw.WorkerSupportsCancellation = true;
            _bw.RunWorkerAsync();
        }

        private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //just for error reporting 
            var item = new ListViewItem();
            item.Text = Path.GetFileName(e.UserState.ToString());
            lvError.Items.Add(item);
        }

        private void ConvertAllCheckedItems(string[] inputFiles, string[] outputFiles)
        {
            for (int i = 0; i < inputFiles.Length; i++)
            {
                ConvertFile(inputFiles[i], outputFiles[i]);
            }
        }

        private void ConvertFile(string inputFile, string outputFile)
        {
            if (string.IsNullOrEmpty(inputFile) || string.IsNullOrEmpty(outputFile)) return;

            MediaFoundationInterop.MFStartup(MediaFoundationInterop.MF_VERSION);
            try
            {
                using (var vr = new VorbisFileReader(inputFile))
                {
                    var samp = new SampleChannel(vr);
                    var ws = new SampleToWaveProvider16(samp);
                    MediaFoundationEncoder.EncodeToAac(ws, outputFile);
                }
            }
            catch (Exception)
            {
                _bw.ReportProgress(0, inputFile);
            }


            MediaFoundationInterop.MFShutdown();
        }

        private string OutputFilePath(ListViewItem item)
        {
            string outputFile = _outputFolder + "\\" + item.Text;
            outputFile = outputFile.Replace(".ogg", ".mp4");
            return outputFile;
        }


        private void CreateOutputFolder()
        {
            _outputFolder = SelectedPath + "\\Mp4";
            txtTargetFolder.Text = _outputFolder;
            if (!Directory.Exists(_outputFolder))
            {
                Directory.CreateDirectory(_outputFolder);
            }

            else
            {
                //delete and create
                var directory = new DirectoryInfo(_outputFolder);
                Empty(directory);
                Directory.CreateDirectory(_outputFolder);
            }
        }

        public static void Empty(DirectoryInfo directory)
        {
            foreach (FileInfo file in directory.GetFiles()) file.Delete();
            foreach (DirectoryInfo subDirectory in directory.GetDirectories()) subDirectory.Delete(true);
        }

        private void fileListView_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            lblChecked.Text = listView1.CheckedItems.Count.ToString();
        }

        private void btnPlay_Click(object sender, EventArgs e)
        {
            if (listView1.CheckedItems.Count <= 0)
            {
                MessageBox.Show(Resources.Form1_btnPlay_Click_No_file_selected);

                return;
            }
            //reset playlist
            _playList.Clear();
            foreach (ListViewItem viewItem in listView1.CheckedItems)
            {
                string fileToPlay = viewItem.Tag.ToString();
                _playList.Enqueue(fileToPlay);
            }

            PlayFile(_playList.Dequeue());
        }

        private void PlayFile(string fileToPlay)
        {
            StopPlayback();
            StartPlayback(fileToPlay);
            btnPlay.Enabled = false;
            btnStop.Enabled = true;
        }

        private void StartPlayback(string fileToPlay)
        {
            _reader = new VorbisFileReader(fileToPlay);

            _waveOut = new WaveOut();
            _waveOut.PlaybackStopped += OnStoppedHandler;
            _waveOut.Init(_reader);
            _waveOut.Play();
        }

        private void OnStoppedHandler(object sender, EventArgs e)
        {
            _waveOut.PlaybackStopped -= OnStoppedHandler;
            _waveOut = null;

            _reader.Dispose();
            _reader = null;
            if (_playList.Count > 0)
            {
                PlayFile(_playList.Dequeue());
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            StopPlayback();
            _playList.Clear();
            btnPlay.Enabled = true;
        }

        private void StopPlayback()
        {
            if (_waveOut != null && _waveOut.PlaybackState == PlaybackState.Playing)
                _waveOut.Stop();
        }

        private void textBox1_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                LoadFiles(textBox1.Text);
            }
        }

        private void btnCheckAll_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem item in listView1.Items)
            {
                item.Checked = true;
            }
        }

        private void btnUnCheckAll_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem item in listView1.Items)
            {
                item.Checked = false;
            }
        }

        private void btnInverseCheck_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem item in listView1.Items)
            {
                if (item.Checked)
                {
                    item.Checked = false;
                }
                else if
                    (item.Checked == false)
                {
                    item.Checked = true;
                }
            }
        }

        //filesystem watching and updating result list
        private void StartFileSystemWatcher()
        {
            fileSystemWatcher1.Path = _outputFolder;

            fileSystemWatcher1.NotifyFilter = NotifyFilters.FileName |
                                              NotifyFilters.LastWrite |
                                              NotifyFilters.Size |
                                              NotifyFilters.DirectoryName;


            fileSystemWatcher1.Created += fileSystemWatcher_Handler;
            fileSystemWatcher1.Changed += fileSystemWatcher_Handler;
            fileSystemWatcher1.Deleted += fileSystemWatcher_Handler;
            fileSystemWatcher1.Renamed += fileSystemWatcher_Handler;


            // START watching
            fileSystemWatcher1.EnableRaisingEvents = true;
        }

        private void StopFileSystemWatcher()
        {
            fileSystemWatcher1.EnableRaisingEvents = false;
        }

        private void fileSystemWatcher_Handler(object sender, FileSystemEventArgs e)
        {
            DisplayFileSystemWatcherInfo(e.ChangeType, e.Name);
        }

        private void DisplayFileSystemWatcherInfo(WatcherChangeTypes watcherChangeTypes, string name,
                                                  string oldName = null)
        {
            listView2.Items.Clear();
            string[] outputFiles = Directory.GetFiles(_outputFolder);
            foreach (string file in outputFiles)
            {
                string fileName = Path.GetFileName(file);
                var item = new ListViewItem(fileName);
                item.Tag = file;

                listView2.Items.Add(item);
            }
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            //SetFocusOnSourcePath();
            this.ActiveControl = label1;
        }

        private void SetFocusOnSourcePath()
        {
            textBox1.Text = Resources.Default_Source_Path;
            textBox1.Focus();
            int start = 0;
            if (!string.IsNullOrEmpty(textBox1.Text)) start = textBox1.Text.Length;
            textBox1.Select(start, 0);
        }
    }
}